package common

import "bytes"
import "time"
import "strings"
import "strconv"
import "fmt"
import "encoding/json"
import "io/ioutil"
import "io"
import "math"
import "log"
import "os"
import "path/filepath"
import "net/http"
import "bufio"
import "os/exec"
import "archive/zip"
import "mime/multipart"

import "dockerServer/gin" // 这个需要合并到common工具中,该源代码在go安装目录的src下

// ------------------------- common version ----------------------
var version string = "v0.1.5"
var desciption string = "插入gin"

/* ----------------------------进程相关----------------------------*/
func simpleExec(args ...string){
    // function : 执行子进程
    // param : 子进程shell命令
    var arg []string
	if len(args) > 1 {
		arg = args[1:]
	}
    cmd := exec.Command(args[0], arg...)
    log.Println("exec command : ",cmd.String())
    err:= cmd.Run()
    if (err!=nil){
        log.Println("exec error : ",err)
    }
}

func stdinExec(input string,args ...string){
	// function : 
	// param args : 子进程shell命令
	// param input : stdin 后续输入,比如Ubuntu权限问题
	var arg []string
	if len(args) > 1 {
		arg = args[1:]
	}
    cmd := exec.Command(args[0], arg...)
	cmd.Stdin = strings.NewReader(input)
    log.Println("exec command : ",cmd.String())
    err:= cmd.Run()
    if (err!=nil){
        log.Println("exec error : ",err)
    }
}

/*-----------------------------网络请求-----------------------------*/
func Get(url string) (string,error){
	// get请求
	res,err:=http.Get(url)
	if err!=nil{
		log.Println("http get address : ",url,"http error : ",err)
	}

	body,err:=ioutil.ReadAll(res.Body)
	return string(body),err
}

func getHTMLResponse(address string)(*http.Response,error){
	// function : 通过一个网址获取该网页的html对象
	// param address : 网页链接
	// return : 一个http的返回对象
	resp,err:=Get(address)
	if err != nil{
		fmt.Println("请求获取html失败 :",err)
	}

	return resp,err
}


func Post(url string, data interface{}, contentType string) string {
    // example:
    // var url string="http://192.168.209.128:8080/paramPostServer?"+quantity
	// data:=map[string]string{"execFilePath":"string aaa","resultPath":"string bbb"}
	// var result string=Post(url,data,"application/json")
    // 超时时间：5秒
    client := &http.Client{Timeout: 5 * time.Second}
    jsonStr, _ := json.Marshal(data)
    resp, err := client.Post(url, contentType, bytes.NewBuffer(jsonStr))
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()

    result, _ := ioutil.ReadAll(resp.Body)
    return string(result)
}

func post(url string)(string,error){
	// function : http post requests
	// TODO : 未完成
	resp, err := http.Post(url,
        "application/x-www-form-urlencoded",
        strings.NewReader("name=hasaki"))
    if err != nil {
        log.Println("--->http post error : ",err)
    }
 
    defer resp.Body.Close()
    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        log.Println("--->translate post respone error : ",err)
		return "",err
    }
 
    return string(body),nil
}

func postFile(uri string, params map[string]string, paramName, path string) (*http.Request, error) {
    // function : 上传单个文件到服务器上
    // param url : 服务器地址
    // param param : form参数
    // param paramName : 服务器上指定的键值,如果服务器上对不上这个名称,服务器会报500
    // param path : 本地文件的路径
    // example
    // path := "D:/pratice/hasakiPic/Mimipigeon/0.jpg"
    // extraParams := map[string]string{
    //    "title":       "My Document",
    // }
    // request, err := newfileUploadRequest("http://127.0.0.1:8080/uploadFile", extraParams, "file", path)
    // if err != nil {
    //    log.Println(err)
    // }
    file, err := os.Open(path)
    if err != nil {
        return nil, err
    }
    defer file.Close()
 
    body := &bytes.Buffer{}
    writer := multipart.NewWriter(body)
    part, err := writer.CreateFormFile(paramName, filepath.Base(path))
    if err != nil {
        return nil, err
    }
    _, err = io.Copy(part, file)
 
    for key, val := range params {
        _ = writer.WriteField(key, val)
    }
    err = writer.Close()
    if err != nil {
        return nil, err
    }
 
    request, err := http.NewRequest("POST", uri, body)
    request.Header.Add("Content-Type", writer.FormDataContentType())
    return request, err
}

func saveFileFromNet(url string,savePath string){
	// function : 保存网络文件到本地
	// param savePath : 保存在本地路径  xxx/xxx.jpg
	// param url : link of file
    res, err := http.Get(url)
    if err != nil {
        log.Println("common function save file from Net error")
        return
	}
	if strings.Index(res.Status,"200")==-1{
		log.Println("save file from net function return status code : ",res.Status)
		return
	}
    defer res.Body.Close()
    // 获得get请求响应的reader对象 Note : 这里有文件大小的限制
    reader := bufio.NewReaderSize(res.Body, 1024 * 1024)
    
    file, err := os.Create(savePath)
    if err != nil {
        panic(err)
    }
    // 获得文件的writer对象
    writer := bufio.NewWriter(file)
    io.Copy(writer, reader)
}

func savePic(url string,savePath string){
	// function : 保存图片到本地
	// param savePath : 图片保存路径  xxx/xxx.jpg
	// param url : 图片网上的地址
    imgPath := savePath
    imgUrl := url
    
    res, err := http.Get(imgUrl)
    if err != nil {
        fmt.Println("common function save picture error")
        return
	}
	if strings.Index(res.Status,"200")==-1{
		fmt.Println("save pictrue function return status code : ",res.Status)
		return
	}
    defer res.Body.Close()
    // 获得get请求响应的reader对象
    reader := bufio.NewReaderSize(res.Body, 64 * 1024)
    
    file, err := os.Create(imgPath)
    if err != nil {
        panic(err)
    }
    // 获得文件的writer对象
    writer := bufio.NewWriter(file)
    io.Copy(writer, reader)
}

func Cors() gin.HandlerFunc {
	// function : 跨域
	// Example for gin framework:
	// ...
	// router := gin.Default()
	// router.Use(Cors())
	// ...
    return func(c *gin.Context) {
        method := c.Request.Method      //请求方法
        origin := c.Request.Header.Get("Origin")        //请求头部
        var headerKeys []string                             // 声明请求头keys
        for k, _ := range c.Request.Header {
            headerKeys = append(headerKeys, k)
        }
        headerStr := strings.Join(headerKeys, ", ")
        if headerStr != "" {
            headerStr = fmt.Sprintf("access-control-allow-origin, access-control-allow-headers, %s", headerStr)
        } else {
            headerStr = "access-control-allow-origin, access-control-allow-headers"
        }
        if origin != "" {
            c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
            c.Header("Access-Control-Allow-Origin", "*")        // 这是允许访问所有域
            c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE,UPDATE")      //服务器支持的所有跨域请求的方法,为了避免浏览次请求的多次'预检'请求
            //  header的类型
            c.Header("Access-Control-Allow-Headers", "Authorization, Content-Length, X-CSRF-Token, Token,session,X_Requested_With,Accept, Origin, Host, Connection, Accept-Encoding, Accept-Language,DNT, X-CustomHeader, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Pragma")
            //              允许跨域设置                                                                                                      可以返回其他子段
            c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers,Cache-Control,Content-Language,Content-Type,Expires,Last-Modified,Pragma,FooBar")      // 跨域关键设置 让浏览器可以解析
            c.Header("Access-Control-Max-Age", "172800")        // 缓存请求信息 单位为秒
            c.Header("Access-Control-Allow-Credentials", "false")       //  跨域请求是否需要带cookie信息 默认设置为true
            c.Set("content-type", "application/json")       // 设置返回格式是json
        }

        //放行所有OPTIONS方法
        if method == "OPTIONS" {
            c.JSON(http.StatusOK, "Options Request!")
        }
        // 处理请求
        c.Next()        //  处理请求
    }
}


/*-----------------------------路径相关----------------------------*/
func exePath()string{
	// function : 返回golang二进制文件的启动位置,golang代码打包之后，这个就会
	// 返回当前程序的路径，如果是通过go run 来运行则会返回一个临时启动的执行路径
	dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
	if err!=nil{
		printError(err)
		return ""
	}
	return dir
}

/*-----------------------------定制日志----------------------------*/
func logging(module string,content string,logFile string){
	// function : 记录普通级别的信息
	// param module : trace/info/warning/error
	// param logFile : 需要写入到文件的路径
	switch module{
	case "trace":
		trace:=log.New(ioutil.Discard,"trace :",log.Ldate|log.Ltime|log.Lshortfile)
		trace.Println(content)
		break
	case "info":
		info:=log.New(os.Stdout,"info :",log.Ldate|log.Ltime|log.Lshortfile)
		info.Println(content)
		break
	case "warning":
		warning:=log.New(os.Stdout,"warning :",log.Ldate|log.Ltime|log.Lshortfile)
		warning.Println(content)
		break
	case "error":
		// 如果用户传过来的地址是个空值的情况就直接打印内容即可
		if len(logFile)<2{
			print(content)
			return
		}
		file,err:=os.OpenFile(logFile,os.O_CREATE|os.O_WRONLY|os.O_APPEND,0666)
		if err!=nil{
			log.Fatalln("common.go logging function failed to open error log file:",err)
		}
		err2:=log.New(io.MultiWriter(file,os.Stderr),"error :",log.Ldate|log.Ltime|log.Lshortfile)
		err2.Println(content)
		break
	default:
		return
	}
}

//---------------------------- 打印相关 ------------------------------------
func print(content interface{}){
	// function : 简化打印
	fmt.Println(content)
}

func debug(msg ...string){
    // 带有时间的打印
    log.Println(msg)
}

func printError(content error){
	// function : 错误提示
	panic(content)
}

func printLocal(content string){
	// function : 写入日志到本地txt文件，日志文件的路径默认在执行文件的同一目录
	dir:=exePath()
	if len(dir)<2{
		return
	}
	err:=mkdir(dir+"/log")
	if err!=nil{
		fmt.Println(err)
	}
}

func dateJudge(firstDate string,lastDate string)(bool){
	/*
	function : 判断前后时间是否一致，如果一致则返回真，否则假
	param firstDate : 第一个时间 格式：2019-12-21 20:05
	param lastDate : 第二个时间
	return : 如果一致则返回真，否则假
	*/
	if firstDate==lastDate{
		return true
	}else{
		return false
	}
}

/*----------------------------------文件操作--------------------------------*/
func readJson(path string)([]map[string]interface{}){
	/*
	function : 读取json文件并转换成[]map类型
	param path : 需要读取的json文件的路径
	return : 返回
	*/
	byteValue, err := ioutil.ReadFile(path)
	if err!=nil{
		fmt.Println("读取json文件报错，读取的文件地址：",path)
	}

	var result []map[string]interface{}
	err = json.Unmarshal(byteValue, &result)
	if err != nil {
		fmt.Println("解析json 错误",err)
	}
	
	return result
}

func writeJson(path string,mapData interface{}){
	// function : 写入json 文件
	// param mapData : map类型数据
	if jsonData,err:=json.Marshal(mapData);err==nil{
		err := ioutil.WriteFile(path, jsonData, 0644)
		fmt.Println("写入json",err)
	}else{
		fmt.Println("结果错误",err)
	}
}

func readJsonForList(path string)([]map[string]interface{}){
	/*
	function : 读取json文件并转换成map类型
	param path : 需要读取的json文件的路径
	return : 返回[]类型的json
	*/
	byteValue, err := ioutil.ReadFile(path)
	if err!=nil{
		log.Println("读取json文件报错，读取的文件地址：",path)
	}

	var result []map[string]interface{}
	
	err = json.Unmarshal(byteValue, &result)
	if err != nil {
		log.Println("解析json 错误",err)
		emptyList:=result
		return emptyList
	}
	
	return result
}

func readCSV(path string)[]map[string]interface{}{
	// function : 从本地加载CSV文件，并返回到上层,读取csv
	// param path : CSV文件路径
	var tempData []map[string]interface{}
	return tempData
}

func mkdir(filePath string)error{
	// function : 创建文件夹
	// param filePath : 文件夹路径
	err:=os.Mkdir(filePath,os.ModePerm)
	return err
}

func getAllFile(pathname string) error {
	// 获取一个目录下面所有的文件
	rd, err := ioutil.ReadDir(pathname)
	for _, fi := range rd {
		if fi.IsDir() {
			log.Printf("[%s]\n", pathname+"\\"+fi.Name())
			getAllFile(pathname + fi.Name() + "\\")
		} else {
			log.Println(fi.Name())
		}
	}
	return err
}

func Unzip(zipFile string, destDir string) error {
	// 压缩文件
	// param zipFile : 需要压缩的文件夹 xxx/abc.zip
	// param destDir : 压缩之后的路径 xxx/abc   unzip file --> xxx/abc/abc 
    zipReader, err := zip.OpenReader(zipFile)
    if err != nil {
        return err
    }
    defer zipReader.Close()

    for _, f := range zipReader.File {
        fpath := filepath.Join(destDir, f.Name)
        if f.FileInfo().IsDir() {
            os.MkdirAll(fpath, os.ModePerm)
        } else {
            if err = os.MkdirAll(filepath.Dir(fpath), os.ModePerm); err != nil {
                return err
            }

            inFile, err := f.Open()
            if err != nil {
                return err
            }
            defer inFile.Close()

            outFile, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
            if err != nil {
                return err
            }
            defer outFile.Close()

            _, err = io.Copy(outFile, inFile)
            if err != nil {
                return err
            }
        }
    }
    return nil
}


/* ----------------------------时间相关的操作----------------------*/
func sleep(n int){
	/*
	function : 睡眠函数
	param n : 睡眠的秒数
	秒:time.Second
	分钟:time.Minute
	小时:time.Hour
	微妙:time.Nanosecond
	*/
	if n<0{
		return
	}

	for i:=0;i<=n;i++{
		time.Sleep(time.Second)
	}
}

func sleepMin(n int){
	// function : 睡眠多少分钟
	// param n : 睡眠的分钟数
	if n<0{
		return
	}
	for i:=0;i<=n;i++{
		time.Sleep(time.Minute)
	}
}

func sleepHour(h int){
	// function : 睡眠的小时数
	if h<0{
		return
	}
	for i:=0;i<h;i++{
		time.Sleep(time.Hour)
	}
}

func nowTime(m string)(string){
	// function : 返回需要的当前时间字符串
	// return : 返回需要的时间字符串
	now:=time.Now().Format("2006-01-02 15:04:05")
	if m=="day"{
		return string([]byte(now[:10]))              // 返回的示例：2019-12-30
	}else{
		now:=time.Now().Unix()
		return string(now)
	}
}

func timeFormat(t time.Time)string{
	// 返回2020-8-4 00:00:00格式的时间字符串
	timeString := t.Format("2006-01-02 15:04:05")
	return timeString
}

func integralPoint()(bool){
	// function : 判断当前时间是否为整点
	// 如果不为整点则返回false
	time:=nowTime("day")
	if strings.Index(time,"00:00:")!=-1{
		return true
	}else{
		return false
	}
}

/* ------------------------------ 算法相关 ----------------------------- */
func listReverse(l []string)  {
    // 对字符串数组进行reverse
    // example:
    // a:=[]string{"1","2","3"}
    // listReverse(a)
    // fmt.Println(a)  --> "3","2","1"
    for i:=0; i < int(len(l)/2) ;i++{
        li := len(l) - i -1
        l[i],l[li] = l[li],l[i]
    }
}

/*--------------------------------数学相关-------------------------------*/
func min(a, b int) int {
	// function : 判断两个整型的大小
	if a <= b {
		return a
	}
	return b
}

func max(a, b int) int {
	// function : 判断两个整型的大小
	if a >= b {
		return a
	}
	return b
}

/*------------------------------- Gin ----------------------------------*/

/*--------------------------------类型转换-------------------------------*/
func strJoin(first string,last string,mid string)(string){
	/*
	function : 拼接合成字符串
	param first : 第一段字符串
	param mid : 中间分割的字符串
	param last : 最后一段字符串
	return : 返回拼接好的字符串
	*/
	return strings.Join([]string{first,last},mid)
}

func intToStr(num int)(string){
	/*
	function : 整型转换成字符串
	param num : 整型
	return : 。。
	*/
	return strconv.Itoa(num)
}

func structToJson(s interface{})string{
	// function : 结构体转json字符串
	// return : 返回json字符串
	var data string
	if dataU,err:=json.MarshalIndent(s,"","    ");err==nil{
		// fmt.Println(fmt.Sprintf("%T",dataU)) []uint8
		data=string(dataU)
	}else{
		fmt.Println("结构体转json结果错误",err)
	}
	return data
}

func float64ToInt(f float64)int{
	// function : float64转int
	return int(math.Floor(f))
}

func uint8ToStr(uint8Data []uint8) string {
	// function : uint8转字符串
	return string(uint8Data)
}

func jsonToMap(jsonStr string) map[string]interface{}{
	// function : json转map
	var returnData map[string]interface{}
	err:=json.Unmarshal([]byte(jsonStr),&returnData)
	if err!=nil{
		fmt.Println("json转map报错",jsonStr,"错误原因:",err)
	}
	return returnData
}

func uint8ToMap(uint8Data []uint8) interface{}{
	// uint8转map
	var returnData map[string]interface{}
	err:=json.Unmarshal(uint8Data,&returnData)
	if err!=nil{
		fmt.Println("uint8转map",uint8Data,err)
	}
	return returnData
}

func mapToByte(mapData map[string]interface{}) []byte {
	// function : map 转 byte数组
	mJson,_:=json.Marshal(mapData)
	mJsonStr:=string(mJson)
	mByte:=[]byte(mJsonStr)
	return mByte
}